#include "SimpleOpt.h"
#include "SimpleGlob.h"

#include <iostream>
using namespace std;
#include <stdio.h>
#include <locale.h>

#include <DoQuMeasures/EvalBleedThroughAdd.hpp>
#include <DoQuMeasures/EvalBleedThroughIntensity2Back.hpp>
#include <DoQuMeasures/EvalBleedThroughIntensity2Ink.hpp>
#include <DoQuMeasures/EvalBleedThroughSubstitutionGrowing.hpp>
#include <DoQuMeasures/EvalBleedThroughVersoQuantity.hpp>
#include <DoQuMeasures/EvalBleedThroughSubstitution.hpp>
#include <DoQuMeasures/EvalBleedThroughParameters.hpp>
#include <DoQuMeasures/MeasuresFacade.hpp>
#include <DoQuMeasures/Exceptions.hpp>

#include <DoQuMeasures/Logger.hpp>
#include <DoQuMeasures/AffineTransform.hpp>
#include <DoQuMeasures/Registration.hpp>

#include <girlTools/girlToolsImage.hpp>
#include <girlTools/girlToolsPngCodec.hpp>
#include <girlTools/girlToolsJpgCodec.hpp>
#include <girlTools/girlToolsPnmCodec.hpp>

#include <vector>
#include <boost/timer.hpp>

#include "StringUtils.h"

# define TCHAR		char
# define _T(x)		x
# define _tprintf	printf
# define _tmain		main
# define _ttoi      atoi



enum { SO_O_MEASURES = 0, SO_O_RECTO = 1 , SO_O_VERSO, SO_O_TRANSLATE,SO_O_ROTATE, SO_O_REGISTER};
CSimpleOpt::SOption g_rgFlags[] =
{
    { SO_O_MEASURES,       _T("--measures"),    SO_MULTI },
    { SO_O_RECTO,          _T("--recto"),       SO_REQ_SEP},
    { SO_O_VERSO,          _T("--verso"),       SO_REQ_SEP},
    { SO_O_TRANSLATE,      _T("--trans"),       SO_MULTI},
    { SO_O_ROTATE,      _T("--rotate"),       SO_MULTI},
    { SO_O_REGISTER,      _T("--register"),       SO_NONE},
    SO_END_OF_OPTIONS
};

static const TCHAR * 
        GetLastErrorText(
                int a_nError
                )
{
    switch (a_nError) {
    case SO_SUCCESS:            return _T("Success");
    case SO_OPT_INVALID:        return _T("Unrecognized option");
    case SO_OPT_MULTIPLE:       return _T("Option matched multiple strings");
    case SO_ARG_INVALID:        return _T("Option does not accept argument");
    case SO_ARG_INVALID_TYPE:   return _T("Invalid argument format");
    case SO_ARG_MISSING:        return _T("Required argument is missing");
    case SO_ARG_INVALID_DATA:   return _T("Invalid argument data");
    default:                    return _T("Unknown error");
    }
}


void parseMeasures(CSimpleOpt &    args, 
                   int             nMultiArgs);

void translate(CSimpleOpt & args,int nMultiArgs);
void rotate(CSimpleOpt & args,int nMultiArgs);
void registerEvaluation(girl::tools::Image &recto, girl::tools::Image &verso);

/*void parseDocuments(CSimpleOpt &    args,
                    int             nMultiArgs);*/


std::vector<std::string> chosenMeasures;
girl::tools::Image verso;
girl::tools::Image recto;

int main(int argc, char * argv[])
{
    int nFlags = SO_O_USEALL;

    bool registe = false;


    girl::tools::PngCodec codec;
        CSimpleOpt args(argc, argv, g_rgFlags, nFlags);
    while (args.Next()) {
        if (args.LastError() != SO_SUCCESS) {
            cout << "Toto tu as tous faux" << endl;
            continue;
        }

        switch (args.OptionId()) {
        case SO_O_MEASURES:
            parseMeasures(args, -1);
            break;
        case SO_O_RECTO:

            cout << "Loading recto : " << args.OptionArg() << std::endl;
            codec.load(args.OptionArg(),recto);

            break;
        case SO_O_VERSO:

            cout << "Loading verso : " << args.OptionArg() << std::endl;
            codec.load(args.OptionArg(),verso);

            break;

        case SO_O_TRANSLATE:
            translate(args, 4); //quatre param (dx, dy, imageEntre, imageSortie)
            break;

        case SO_O_ROTATE:
            rotate(args, 3); //trois param (angle, imageEntre, imageSortie)
            break;

        case SO_O_REGISTER :
            registe = true;
            break;
        }
    }

    if(registe)
    {

        registerEvaluation(recto,verso);

    }

    MeasuresFacade facade;

    for(int i = 0 ; i < chosenMeasures.size(); i++)
    {
        std::cout << chosenMeasures.at(i) << std::endl;
        try{
            MesureTemplateMethod * method = facade.selectMeasure(chosenMeasures.at(i));
            MeasureParameters *params = method->creatParameters();
            if(params->contains("verso")){
                params->setParamByName("verso",&verso);
            }
            method->initWithParameters(params);
            cout << "RESULT : " << method->evaluate(recto) << std::endl;
        }catch(Exceptions::MessageException e)
        {
            cout << chosenMeasures.at(i) << "not selected " << endl;
            cout << e.getMessage() << endl;
        }
    }

    //facade.measure(recto);

    return 0;
}

void parseMeasures(CSimpleOpt &    args, 
                   int             nMultiArgs)
{
    char ** rgpszArg = NULL;

    // get the number of arguments if necessary
    if (nMultiArgs == -1) {
        // first arg is a count of how many we have
        rgpszArg = args.MultiArg(1);
        if (!rgpszArg) {
            _tprintf(
                    _T("%s: '%s' (use --help to get command line help)\n"),
                    GetLastErrorText(args.LastError()), args.OptionText());
            return;
        }

        nMultiArgs = _ttoi(rgpszArg[0]);
    }
    _tprintf(_T("%s: expecting %d args\n"), args.OptionText(), nMultiArgs);

    // get the arguments to follow
    rgpszArg = args.MultiArg(nMultiArgs);
    if (!rgpszArg) {
        _tprintf(
                _T("%s: '%s' (use --help to get command line help)\n"),
                GetLastErrorText(args.LastError()), args.OptionText());
        return;
    }

    for (int n = 0; n < nMultiArgs; ++n) {
        //_tprintf(_T("MultiArg %d: %s\n"), n, rgpszArg[n]);
        chosenMeasures.push_back(std::string(rgpszArg[n]));
    }

}

void translate(CSimpleOpt & args,int nMultiArgs)
{

    char ** rgpszArg = NULL;

    // get the number of arguments if necessary
    if (nMultiArgs == -1) {
        // first arg is a count of how many we have
        rgpszArg = args.MultiArg(1);
        if (!rgpszArg) {
            _tprintf(
                    _T("%s: '%s' (use --help to get command line help)\n"),
                    GetLastErrorText(args.LastError()), args.OptionText());
            return;
        }

        nMultiArgs = _ttoi(rgpszArg[0]);
    }
    //_tprintf(_T("%s: expecting %d args\n"), args.OptionText(), nMultiArgs);

    // get the arguments to follow
    rgpszArg = args.MultiArg(nMultiArgs);
    if (!rgpszArg) {
        _tprintf(
                _T("%s: '%s' (use --help to get command line help)\n"),
                GetLastErrorText(args.LastError()), args.OptionText());
        return;
    }

    std::string dx = "-5"; //rgpszArg[0];
    std::string dy = "3"; //rgpszArg[1];
    std::string imageName = rgpszArg[2];
    std::string outName = rgpszArg[3];
    //cout << dx << " " << dy << " " << imageName << " " << outName << endl;

    girl::tools::JpgCodec codec;
    girl::tools::PngCodec codec2;

    girl::tools::Image myImage;
    codec2.load(imageName,myImage);

    girl::tools::Image out;
    AffineTransform::translated(myImage,convertToDouble(dx),convertToDouble(dy),out);
    codec.save(outName,out);
}


void rotate(CSimpleOpt & args,int nMultiArgs)
{

    char ** rgpszArg = NULL;

    // get the number of arguments if necessary
    if (nMultiArgs == -1) {
        // first arg is a count of how many we have
        rgpszArg = args.MultiArg(1);
        if (!rgpszArg) {
            _tprintf(
                    _T("%s: '%s' (use --help to get command line help)\n"),
                    GetLastErrorText(args.LastError()), args.OptionText());
            return;
        }

        nMultiArgs = _ttoi(rgpszArg[0]);
    }
    //_tprintf(_T("%s: expecting %d args\n"), args.OptionText(), nMultiArgs);

    // get the arguments to follow
    rgpszArg = args.MultiArg(nMultiArgs);
    if (!rgpszArg) {
        _tprintf(
                _T("%s: '%s' (use --help to get command line help)\n"),
                GetLastErrorText(args.LastError()), args.OptionText());
        return;
    }

    std::string angle = rgpszArg[0];
    std::string imageName = rgpszArg[1];
    std::string outName = rgpszArg[2];
    //cout << dx << " " << dy << " " << imageName << " " << outName << endl;

    girl::tools::JpgCodec codec;
    girl::tools::PngCodec codec2;

    girl::tools::Image myImage;
    codec2.load(imageName,myImage);

    girl::tools::Image out;
    AffineTransform::rotated(myImage,convertToDouble(angle),out);
    codec.save(outName,out);
}

void registerEvaluation(girl::tools::Image &recto, girl::tools::Image &verso)
{
    Registration * blockRegistration = new BlockRegistration(recto,verso);
    Registration * centeredRegistration = new CenteredBlockRegistration(recto,verso);
    Registration * resizedRegistration = new ResizedRegistration(recto,verso,4);
    Registration * registration = new Registration(recto,verso);

    std::cout << "EVALUATION REGISTRATION --------------------------------------" << std::endl;
    {
        boost::timer timer;
        Transformation * trans = registration->registerTransformation();
        std::cout << "registration took : " << timer.elapsed() << std::endl;

        std::cout << "Transformation : " <<
                "angle : " << trans->getParam().angle << std::endl <<
                "dx : " << trans->getParam().dx << std::endl <<
                "dy : " << trans->getParam().dy << std::endl;

        girl::tools::Image transformed = trans->transformed(verso);
        girl::tools::JpgCodec codec;
        std::stringstream stream;
        stream << "transformed_registration" << ".jpg" ;
        std::string str;
        stream >> str;
        codec.save(str,transformed);

        delete trans;
        delete registration;
    }

    std::cout << "END EVALUATION --------------------------------------" << std::endl;


    std::cout << "EVALUATION BlockRegistration --------------------------------------" << std::endl;
    {
        boost::timer timer;
        Transformation * trans = blockRegistration->registerTransformation();
        std::cout << "BlockRegistration took : " << timer.elapsed() << std::endl;

        std::cout << "Transformation : " <<
                "angle : " << trans->getParam().angle << std::endl <<
                "dx : " << trans->getParam().dx << std::endl <<
                "dy : " << trans->getParam().dy << std::endl;

        girl::tools::Image transformed = trans->transformed(verso);
        girl::tools::JpgCodec codec;
        std::stringstream stream;
        stream << "transformed_blockRegistration" << ".jpg" ;
        std::string str;
        stream >> str;
        codec.save(str,transformed);

        delete trans;
        delete blockRegistration;
    }

    std::cout << "END EVALUATION --------------------------------------" << std::endl;


    std::cout << "EVALUATION centeredRegistration --------------------------------------" << std::endl;
    {
        boost::timer timer;
        Transformation * trans = centeredRegistration->registerTransformation();
        std::cout << "registration took : " << timer.elapsed() << std::endl;

        std::cout << "Transformation : " <<
                "angle : " << trans->getParam().angle << std::endl <<
                "dx : " << trans->getParam().dx << std::endl <<
                "dy : " << trans->getParam().dy << std::endl;

        girl::tools::Image transformed = trans->transformed(verso);
        girl::tools::JpgCodec codec;
        std::stringstream stream;
        stream << "transformed_centeredRegistration" << ".jpg" ;
        std::string str;
        stream >> str;
        codec.save(str,transformed);

        delete trans;
        delete centeredRegistration;
    }

    std::cout << "END EVALUATION --------------------------------------" << std::endl;

    std::cout << "EVALUATION resizedRegistration --------------------------------------" << std::endl;
    {
        boost::timer timer;
        Transformation * trans = resizedRegistration->registerTransformation();
        std::cout << "registration took : " << timer.elapsed() << std::endl;

        std::cout << "Transformation : " <<
                "angle : " << trans->getParam().angle << std::endl <<
                "dx : " << trans->getParam().dx << std::endl <<
                "dy : " << trans->getParam().dy << std::endl;

        girl::tools::Image transformed = trans->transformed(verso);
        girl::tools::JpgCodec codec;
        std::stringstream stream;
        stream << "transformed_resizedRegistration" << ".jpg" ;
        std::string str;
        stream >> str;
        codec.save(str,transformed);

        delete trans;
        delete resizedRegistration;
    }

    std::cout << "END EVALUATION --------------------------------------" << std::endl;
}

/*void parseDocuments(CSimpleOpt &    args,
                    int             nMultiArgs)
{
    char ** rgpszArg = NULL;

    // get the number of arguments if necessary
    if (nMultiArgs == -1) {
        // first arg is a count of how many we have
        rgpszArg = args.MultiArg(1);
        if (!rgpszArg) {
            _tprintf(
                    _T("%s: '%s' (use --help to get command line help)\n"),
                    GetLastErrorText(args.LastError()), args.OptionText());
            return;
        }

        nMultiArgs = _ttoi(rgpszArg[0]);
    }
    _tprintf(_T("%s: expecting %d args\n"), args.OptionText(), nMultiArgs);

    // get the arguments to follow
    rgpszArg = args.MultiArg(nMultiArgs);
    if (!rgpszArg) {
        _tprintf(
                _T("%s: '%s' (use --help to get command line help)\n"),
                GetLastErrorText(args.LastError()), args.OptionText());
        return;
    }

    for (int n = 0; n < nMultiArgs; ++n) {
        //_tprintf(_T("MultiArg %d: %s\n"), n, rgpszArg[n]);
        docs.push_back(std::string(rgpszArg[n]));
    }
}*/
